<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
	<head>
		<title>htn format</title>		
	</head>
	<body>
		
	<pre id="clean">&lt;html&gt;   
    &lt;head&gt;
        &lt;title&gt;Lets clean this up&lt;/title&gt;
    &lt;/head&gt;  
    &lt;body&gt;
    
    &lt;div class='test'&gt;
    
    &lt;h1&gt;We gota format itclean it up&lt;/h1&gt;
    &lt;p&gt; CLeanzup&lt;span&gt;yeahho&lt;/span&gt;&lt;b&gt;W!&lt;/b&gt;&lt;/p&gt;  &lt;p&gt; CLeanzup&lt;span&gt;yeahho&lt;/span&gt;&lt;b&gt;W!&lt;/b&gt;&lt;/p&gt;    
  
  
  
    &lt;ul class='test'&gt; list 1 yeay
  
    &lt;li class='test'&gt; list 1 yeay
     &lt;/li&gt;
  
    &lt;li class='test'&gt; list 2 yeay
     &lt;/li&gt;
  
    &lt;li class='test'&gt; list 3 yeay
     &lt;/li&gt;
     &lt;/ul&gt;
  
   &lt;/div&gt;
   
    &lt;/body&gt;
    &lt;/html&gt;


	</pre>

        <script>	  
	  
	  blockElements  = ["address", "blockquote", "center", "dir", "div", "dl", "fieldset", "form", "h1", "h2", 
						"h3", "h4", "h5", "h6", "hr", "isindex", "menu", "noframes", "noscript", "ol", 
						"p", "pre", "table", "ul", "dd", "dt", "li", "tbody", "td", "tfoot", "th", "thead",
						"tr", "button", "del", "ins", "map", "object", "script", "html", "body", "head", 
						"title","script", "link"];
			//need another list that has li, title, p   etc that doesnt put a new line before the closing tag			
						
        function log(m){ console.log(m)};
        var pre = document.getElementById("clean");
        var output = pre.innerHTML.replace(/&lt;/g, "<").replace(/&gt;/g, ">");
    
        var tokens = tokenize(output);
        
        function tokenize(html) {
    
            var node = /<\/?[^>]+>/,
                textNode = /[^<]*(?=<)/,
                tokens = [],
                count = 1000;               
                
            html = html.replace(/>\s+</g, "><");
            html = html.replace(/\n/g, "");
            while(html.length > 0) {
                
                //find a node
                if(html.match(node)) {
                    //log(html.match(node))
                    tokens.push(html.match(node)[0]);
                    html = html.replace(node, "");
                }
                
                //find a textnode
                if(html.match(textNode)) {
                    //log(html.match(textNode));
                    tokens.push(html.match(textNode)[0]);
                    html = html.replace(textNode, "");
                }         
                       
                //stop looping
                if(count-- < 0 || tokens[tokens.length-1] == "</html>") 
                    break;
                      
            }
        
            log(html);
            return tokens;
        }
        
        function cleanTokenizedWhitespace(tokens) {
            var clean = [];
            //need to not do for pre
            for(var i = 0, len = tokens.length; i<len; i++) {
                if(tokens[i].length > 0) {
                    clean.push(tokens[i]);
                }
            }
            return clean;
        
        }
        function indentTokens(tokens) {
            var stack = [];
            function isTextNode(s) { return s[0] !== "<"}
            function isBlock(s) {    return RegExp("<\/?("+blockElements.join("|")+")").test(s)}          
            function isCloser(s) { return s[1] === "/"}
            function buildTabs(n) { return (function(){var ts = ""; while(ts.length < n) ts+="\t";return ts;  } )()  }  
            Array.prototype.peek = function() {return this.length>0 ? this[this.length-1] : undefined;};
            
            tokens = cleanTokenizedWhitespace(tokens);                    
        
            //need to not do for pre
            for(var i = 0, len = tokens.length; i<len; i++) {
                var tok = tokens[i];
                console.log(tok, "  Block:", isBlock(tok), "Text node:", isTextNode(tok), "Closer:", isCloser(tok));
                console.log("Stack: ", stack);
                //need to work out if its a closing or poening
                if(!isTextNode(tok) && isBlock(tok) && !isCloser(tok)) {
                    log("Add ", tok, "to stack");
                    stack.push(tok);
                    tokens[i] = "\n" + buildTabs(stack.length-1) + tok ;                    
                }                  
                else if(!isTextNode(tok) && isBlock(tok) && isCloser(tok)) { //if it is the same as the tpo level we can remvoe it from the stack and go down an indent
                    stack.pop();                 
                    tokens[i] = "\n" + buildTabs(stack.length) + tok;  
                }
                
                
            }
        
            //maintain a stack, the depth of the stack is sthe number of indents...
            log(tokens);
            return tokens;
        }
        
        tokens = indentTokens(tokens);
        
        
        
        
        pre.innerHTML += "<hr>" + tokens.join("").replace(/</g, "&lt;").replace(/>/g, "&gt;");
        </script>

    </body>
</html>
